/**
 * Copyright (c) 2022 Shanghai Fullhan Microelectronics Co., Ltd.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 *
 * Change Logs:
 */

#include <common.h>
#include <environment.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <usb.h>
#include <phy.h>
#include <spi.h>
#include <dwc3-uboot.h>
#include <mmc.h> 
DECLARE_GLOBAL_DATA_PTR;
#define MAX_MAC_CTL    1
#define CONFIG_GPIO_EMAC_0_PHY_RESET	128
struct phy_reg_cfg phy_config_array[] = {
	{
		.id = FH_GMAC_PHY_RTL8201,
		.list = {
			{W_PHY, 0x1f, 0x7},
			{W_PHY, 0x10, 0x1ffc},
			{W_PHY, 0x1f, 0},
			{W_PHY, 0x0d, 0x07},
			{W_PHY, 0x0e, 0x3c},
			{W_PHY, 0x0d, 0x4007},
			{W_PHY, 0x0e, 0x0},
			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_RMII,
	},
	{
		.id = FH_GMAC_PHY_INTERNAL_V2,
		.list = {
			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_MII,
	},
	{0},
};


/* Get boot mode */
int get_boot_media(void)
{
	int boot_mode;

	boot_mode = GET_REG(REG_PMU_BOOT_MODE);

	return boot_mode;
}

void fh_external_phy_0_reset(void)
{
	gpio_direction_output(CONFIG_GPIO_EMAC_0_PHY_RESET, 1);
	udelay(150000);
	gpio_direction_output(CONFIG_GPIO_EMAC_0_PHY_RESET, 0);
	udelay(10000);
	gpio_direction_output(CONFIG_GPIO_EMAC_0_PHY_RESET, 1);
	udelay(100000);
}

int gmac_0_set_spd(int speed){
	int reg;
	//TBD
	//soc just need to set mac control reg
}


int phy_0_sel_func(struct phy_device *phydev, int phy_sel)
{

	if(phy_sel == EXTERNAL_PHY){
		//external phy sel
		//external phy enable
		SET_REG_M(0x24100178, 1, 0x3);
		//open external phy clk
		SET_REG_M(0x24100178, 1 << 20, 1 << 20);
	}
	else{
		//close external phy clk
		SET_REG_M(0x24100178, 0 << 20, 1 << 20);
		//internal phy sel
		//enable internal phy
		SET_REG_M(0x24100178, 0, 0x3);
		//open internal phy clk
		SET_REG_M(0x2410017c, 1 << 20, 1 << 20);
		//ephy reset
		SET_REG_M(0x24100178, 1 << 4, 1 << 4);
		//maybe need delay?
		SET_REG_M(0x24100178, 0, 1 << 4);
	}
}


static struct gmac_plat_info gmac_plat_data[] = {

	{
		.regs = GMAC0_REG_BASE,
		.id = 0,
		.phy_driver_list[0] = "Generic PHY 0",
		.phy_driver_list[1] = 0,
		.p_cfg_array = phy_config_array,
	},
};

void *get_platform_eth_info(unsigned int id){
	return &gmac_plat_data[id];
}


int phy_0_inf_set(struct phy_device *phydev, int inf){

	if (inf == PHY_INTERFACE_MODE_RGMII){
		printf("unsupport RGMII...\n");
		return -1;
	}

	if(inf == PHY_INTERFACE_MODE_RMII){
		//rmii interface select, [15:13] b'100 == rmii
		SET_REG_M(0x24100178, 0x8000, 0xe000);
	}
	else{
		SET_REG_M(0x24100178, 0, 0xe000);
	}
	return 0;
}


#define BITS_PER_LONG 32
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))

static inline unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
{
	unsigned long idx;

	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx])
			return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
	}

	return size;
}


unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
{
	if (size) {
		unsigned long val = BITMAP_LAST_WORD_MASK(size);
		unsigned long idx = (size-1) / BITS_PER_LONG;

		do {
			val &= addr[idx];
			if (val)
				return idx * BITS_PER_LONG + __fls(val);

			val = ~0ul;
		} while (idx--);
	}
	return size;
}

enum {
	CP_VAL_0,
	CP_VAL_1,
	VAL_100M_0,
	VAL_100M_1,
	VAL_10M_0,
	VAL_10M_1,
	TRAIN_MAX_SIZE,
};

static unsigned int train_100_array[] = {
	31, 30, 29, 28,
	27, 26, 25, 24,
	23, 22, 21, 20,
	19, 18, 17, 16,
	0,  1,  2,  3,
	4,  5,  6,  7,
	8,  9,  10, 11,
	12, 13, 14, 15,
};

static unsigned int train_10_array[] = {
	31, 30, 29, 28,
	27, 26, 25, 24,
	23, 22, 21, 20,
	19, 18, 17, 16,
	0,  1,  2,  3,
	4,  5,  6,  7,
	8,  9,  10, 11,
	12, 13, 14, 15,
};

static unsigned int train_cp_array[] = {
	0, 1, 2, 3,
};
#define REFIX_CP_TRAIN_DATA_OFFSET	    (0)
#define REFIX_100M_TRAIN_DATA_OFFSET    (0)
#define REFIX_10M_TRAIN_DATA_OFFSET	    (0)

struct s_train_val train_array[TRAIN_MAX_SIZE] = {

		[CP_VAL_0].name = "cp_val_0",
		[CP_VAL_0].src_add = REG_PMU_EPHY_PARAM,
		[CP_VAL_0].src_mask = 0xc0,
		[CP_VAL_0].src_vaild_index = 15,
		[CP_VAL_0].dst_add = 0x18,
		[CP_VAL_0].dst_vaild_index = 22,
		[CP_VAL_0].bind_train_array = train_cp_array,
		[CP_VAL_0].bind_train_size = ARRAY_SIZE(train_cp_array),
		[CP_VAL_0].usr_train_offset =
		REFIX_CP_TRAIN_DATA_OFFSET,

		[CP_VAL_1].name = "cp_val_1",
		[CP_VAL_1].src_add = REG_PMU_EPHY_PARAM,
		[CP_VAL_1].src_mask = 0xc0,
		[CP_VAL_1].src_vaild_index = 15,
		[CP_VAL_1].dst_add = 0xb7c,
		[CP_VAL_1].dst_vaild_index = 6,
		[CP_VAL_1].bind_train_array = train_cp_array,
		[CP_VAL_1].bind_train_size = ARRAY_SIZE(train_cp_array),
		[CP_VAL_1].usr_train_offset =
		REFIX_CP_TRAIN_DATA_OFFSET,

		[VAL_100M_0].name = "val_100M_0",
		[VAL_100M_0].src_add = REG_PMU_EPHY_PARAM,
		[VAL_100M_0].src_mask = 0x1f,
		[VAL_100M_0].src_vaild_index = 5,
		[VAL_100M_0].dst_add = 0x24,
		[VAL_100M_0].dst_vaild_index = 8,
		[VAL_100M_0].bind_train_array = train_100_array,
		[VAL_100M_0].bind_train_size = ARRAY_SIZE(train_100_array),
		[VAL_100M_0].usr_train_offset =
		REFIX_100M_TRAIN_DATA_OFFSET,

		[VAL_100M_1].name = "val_100M_1",
		[VAL_100M_1].src_add = REG_PMU_EPHY_PARAM,
		[VAL_100M_1].src_mask = 0x1f,
		[VAL_100M_1].src_vaild_index = 5,
		[VAL_100M_1].dst_add = 0x1308,
		[VAL_100M_1].dst_vaild_index = 8,
		[VAL_100M_1].bind_train_array = train_100_array,
		[VAL_100M_1].bind_train_size = ARRAY_SIZE(train_100_array),
		[VAL_100M_1].usr_train_offset =
		REFIX_100M_TRAIN_DATA_OFFSET,

		[VAL_10M_0].name = "val_10M_0",
		[VAL_10M_0].src_add = REG_PMU_EPHY_PARAM,
		[VAL_10M_0].src_mask = 0x1f00,
		[VAL_10M_0].src_vaild_index = 13,
		[VAL_10M_0].dst_add = 0x24,
		[VAL_10M_0].dst_vaild_index = 16,
		[VAL_10M_0].bind_train_array = train_10_array,
		[VAL_10M_0].bind_train_size = ARRAY_SIZE(train_10_array),
		[VAL_10M_0].usr_train_offset =
		REFIX_10M_TRAIN_DATA_OFFSET,

		[VAL_10M_1].name = "val_10M_1",
		[VAL_10M_1].src_add = REG_PMU_EPHY_PARAM,
		[VAL_10M_1].src_mask = 0x1f00,
		[VAL_10M_1].src_vaild_index = 13,
		[VAL_10M_1].dst_add = 0x1308,
		[VAL_10M_1].dst_vaild_index = 0,
		[VAL_10M_1].bind_train_array = train_10_array,
		[VAL_10M_1].bind_train_size = ARRAY_SIZE(train_10_array),
		[VAL_10M_1].usr_train_offset =
		REFIX_10M_TRAIN_DATA_OFFSET,
};

void dump_phy_train_val(struct s_train_val *p_train)
{
	printf("------------GAP_LINE---------------\n");
	printf("name            : %s\n", p_train->name);
	printf("src_add         : %08x\n", p_train->src_add);
	printf("src_mask        : %08x\n", p_train->src_mask);
	printf("src_vaild_index : %08x\n", p_train->src_vaild_index);
	printf("dst_add         : %08x\n", p_train->dst_add);
	printf("dst_vaild_index : %08x\n", p_train->dst_vaild_index);
	printf("usr_train_offset: %d\n", p_train->usr_train_offset);
}


void parse_train_data(struct s_train_val *p_train)
{
	unsigned long test_data;
	int i;
	unsigned int temp_data;
	unsigned int vaild_bit_pos;
	int max_offset, low_offset;
	unsigned int *array;
	unsigned int size;

	array = p_train->bind_train_array;
	size = p_train->bind_train_size;
	test_data = p_train->src_mask;
	vaild_bit_pos = find_first_bit(&test_data, 32);

	if ((!array) ||
	((p_train->src_mask >> vaild_bit_pos) != (size - 1))) {
		printf("para error..\n");
		return;
	}

	temp_data = GET_REG(p_train->src_add);
	if (temp_data & (1 << p_train->src_vaild_index)) {
		/*got vaild val*/
		temp_data &= p_train->src_mask;
		temp_data >>= vaild_bit_pos;
		printf("[%s] :: train val %d\n", p_train->name, temp_data);
		/*find index*/
		for (i = 0; i < size; i++) {
			if (temp_data == array[i]) {
				max_offset = (size - 1) - i;
				low_offset = 0 - i;
				break;
			}
		}

		if ((p_train->usr_train_offset < low_offset) ||
		(p_train->usr_train_offset > max_offset)) {
			printf("offset [%d] should limit in [%d , %d]\n",
			p_train->usr_train_offset, low_offset, max_offset);
			return;
		}
		//fix array idx..
		i += p_train->usr_train_offset;
		temp_data = array[i];
		/*printf("fix idx :: [%d] = %d\n", i, temp_data);*/
	} else {
		temp_data = 0;
		printf("[%s] :: use default train data %d\n",
		p_train->name, temp_data);
	}
	temp_data &= (p_train->src_mask >> vaild_bit_pos);
	SET_REG_M(REG_EPHY_BASE + p_train->dst_add,
	temp_data << p_train->dst_vaild_index,
	(p_train->src_mask >> vaild_bit_pos) << p_train->dst_vaild_index);
}


static void fh_internal_phy_reset(void){
	int idx;

	printf("fh internal phy reset go...\n ");
	SET_REG_M(REG_EPHY_BASE + 0, 0, 0x3<<7);
	SET_REG(REG_EPHY_BASE + 0x28, 0x00247875);
	SET_REG(REG_EPHY_BASE + 0x1274, 0x3c3);

	SET_REG_M(REG_EPHY_BASE + 0x1370, 0x0, 1<<3);
	SET_REG(REG_EPHY_BASE + 0x1274, 0x3c3);
	SET_REG(REG_EPHY_BASE + 0x0b6C, 0xf04);
	SET_REG(REG_EPHY_BASE + 0x1340, 0x0000);
	SET_REG(REG_EPHY_BASE + 0x0bA8, 0x20);
	SET_REG(REG_EPHY_BASE + 0x0018, 0x00c02800);
	SET_REG(REG_EPHY_BASE + 0x001c, 0x00810000);
	SET_REG(REG_EPHY_BASE + 0x0020, 0x0000340c);
	SET_REG(REG_EPHY_BASE + 0x0024, 0x10131500);
	SET_REG(REG_EPHY_BASE + 0x0028, 0x01402800);
	SET_REG(REG_EPHY_BASE + 0x001c, 0xa1800000);
	SET_REG(REG_EPHY_BASE + 0x0028, 0x00402800);
	SET_REG(REG_EPHY_BASE + 0x0014, 0x06000000);
	SET_REG(REG_EPHY_BASE + 0x0020, 0x2340c);
	SET_REG(REG_EPHY_BASE + 0x000c, 0xba0edfff);
	SET_REG(REG_EPHY_BASE + 0x0010, 0x7fcbf9de);
	SET_REG(REG_EPHY_BASE + 0x0b98, 0xd150);
	SET_REG(REG_EPHY_BASE + 0x1224, 0);
	SET_REG(REG_EPHY_BASE + 0x12f0, 0);
	SET_REG(REG_EPHY_BASE + 0x0bc0, 0x7de);
	SET_REG(REG_EPHY_BASE + 0x0bc8, 0x09de);
	SET_REG(REG_EPHY_BASE + 0x070c, 0x0401);
	SET_REG(REG_EPHY_BASE + 0x0a84, 0x1d20);
	SET_REG(REG_EPHY_BASE + 0x06d4, 0x3130);
	SET_REG(REG_EPHY_BASE + 0x0a6c, 0x0818);
	SET_REG(REG_EPHY_BASE + 0x0aec, 0x1000);
	SET_REG(REG_EPHY_BASE + 0x0658, 0x1c00);
	SET_REG(REG_EPHY_BASE + 0x07d4, 0x6900);
	SET_REG(REG_EPHY_BASE + 0x06b8, 0x4);
	SET_REG(REG_EPHY_BASE + 0x06E4, 0x04a0);
	SET_REG(REG_EPHY_BASE + 0x0BBC, 0x1759);
	SET_REG(REG_EPHY_BASE + 0x0bf8, 0x0100);
	SET_REG(REG_EPHY_BASE + 0x0794, 0x9000);
	SET_REG(REG_EPHY_BASE + 0x0788, 0xb);
	SET_REG(REG_EPHY_BASE + 0x1378, 0x1820);
	SET_REG_M(REG_EPHY_BASE + 0x0, 0x23<<24, 0xff<<24);
	SET_REG_M(REG_EPHY_BASE + 0x4, 0x18, 0xff);
	SET_REG(REG_EPHY_BASE + 0x0680, 0x0552);
	SET_REG(REG_EPHY_BASE + 0x1274, 0);
	SET_REG(REG_EPHY_BASE + 0x0b6C, 0x704);
	SET_REG(REG_EPHY_BASE + 0x1340, 0);
	SET_REG(REG_EPHY_BASE + 0x0ba8, 0);
	SET_REG(REG_EPHY_BASE + 0x06c8, 0xc000);
	SET_REG(REG_EPHY_BASE + 0x06e0, 0x8508);
	SET_REG(REG_EPHY_BASE + 0x1360, 0x0137);

	SET_REG(REG_EPHY_BASE + 0x076c, 0x0430);
	SET_REG(REG_EPHY_BASE + 0x06b4, 0xff00);

	SET_REG_M(REG_EPHY_BASE + 0x0778, 0x24 << 8, 0xff << 8);
	SET_REG_M(REG_EPHY_BASE + 0x077c, 0x24, 0xff);

	for (idx = 0; idx < ARRAY_SIZE(train_array); idx++) {
		/*dump_phy_train_val(&train_array[idx]);*/
		parse_train_data(&train_array[idx]);
	}
}

static void fh_internal_phy_link_up_cb(void *pri)
{
	SET_REG(REG_EPHY_BASE + 0x0b10, 0x5000);
	SET_REG(REG_EPHY_BASE + 0x0b14, 0x8000);
	SET_REG(REG_EPHY_BASE + 0x0b1c, 0x000e);
	SET_REG(REG_EPHY_BASE + 0x0b20, 0x0004);
	SET_REG(REG_EPHY_BASE + 0x0aec, 0x1800);
	SET_REG(REG_EPHY_BASE + 0x0b10, 0x0000);
	SET_REG(REG_EPHY_BASE + 0x0b14, 0x0000);
}

static void fh_internal_phy_link_down_cb(void *pri)
{

}

static void fh_external_phy_link_up_cb(void *pri)
{
	struct phy_device *p_phy;

	p_phy = (struct phy_device *)pri;
	if(!p_phy)
		return;

	if(p_phy->phy_id == FH_GMAC_PHY_RTL8201){
		p_phy->bus->write(p_phy->bus, 0, 0, 31, 7);
		p_phy->bus->write(p_phy->bus, 0, 0, 19, 0xc00c);
		if(p_phy->speed == 100)
			p_phy->bus->write(p_phy->bus, 0, 0, 17, 0x28);
		else if(p_phy->speed == 10)
			p_phy->bus->write(p_phy->bus, 0, 0, 17, 0x18);

		p_phy->bus->write(p_phy->bus, 0, 0, 31, 0);
	}
}

static void fh_external_phy_link_down_cb(void *pri)
{

}
static struct phy_driver phy_usr_driver_obj[] = {
	{
		.name = "Generic PHY 0",

		.phy_info[0].phy_sel = INTERNAL_PHY,
		.phy_info[0].phy_reset = fh_internal_phy_reset,
		//fullhan ephy clk don't need sync with mac
		.phy_info[0].sync_mac_spd = 0,
		.phy_info[0].phy_inf = PHY_INTERFACE_MODE_MII,
		.phy_info[0].brd_link_up_cb = fh_internal_phy_link_up_cb,
		.phy_info[0].brd_link_down_cb = fh_internal_phy_link_down_cb,


		.phy_info[1].phy_sel = EXTERNAL_PHY,
		.phy_info[1].phy_reset = fh_external_phy_0_reset,
		.phy_info[1].sync_mac_spd = gmac_0_set_spd,
		.phy_info[1].phy_inf = PHY_INTERFACE_MODE_RMII,
		.phy_info[1].brd_link_up_cb = fh_external_phy_link_up_cb,
		.phy_info[1].brd_link_down_cb = fh_external_phy_link_down_cb,
		.inf_set = phy_0_inf_set,
		.phy_sel = phy_0_sel_func,
	},

};


int board_eth_init(bd_t *bis)
{
	int rc = 0;

#ifdef CONFIG_FH_QOS_GMAC
	int i;
	for(i = 0; i < MAX_MAC_CTL; i++)
		genphy_init_with_usr_cfg(&phy_usr_driver_obj[i]);

	for(i = 0; i < MAX_MAC_CTL; i++){
		if(fh_qos_mac_initialize(bis))
				rc++;
	}
#endif

	return rc;
 }

static void spll_fix_to_297_1m(void)
{
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x184, 0xcc, 0xff);
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x188, 0x06302002, 0x07f7e07e);

	SET_REG_M(CEN_GLB_APB_REG_BASE+0x188, 0, BIT(7)); //pwron = 0
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x188, BIT(0), BIT(0)); //soft_rst = 1
	//gear_shift = 1
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x188, BIT(19), BIT(19));
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x184, 0, BIT(11)); //enp = 0
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x184, 0, BIT(10)); //enr = 0

	SET_REG_M(CEN_GLB_APB_REG_BASE+0x188, BIT(7), BIT(7)); //pwron = 1
	udelay(4);
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x188, 0, BIT(0)); //soft_rst = 0
	udelay(16);
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x188, 0, BIT(19)); //gear_shift = 0
	mdelay(1);
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x184, BIT(11), BIT(11)); //enp = 1
	SET_REG_M(CEN_GLB_APB_REG_BASE+0x184, BIT(10), BIT(10)); //enr = 1
}

static void clock_init(void)
{
	SET_REG_M(REG_PERI_CLK_CTRL,
		CKG_UART0_EN|CKG_WDT0_EN|CKG_XTL_TMR0_EN|CKG_RTC_TMR0_EN|CKG_SYST_EN,
		CKG_UART0_EN|CKG_WDT0_EN|CKG_XTL_TMR0_EN|CKG_RTC_TMR0_EN|CKG_SYST_EN);
	SET_REG_M(REG_PERI_APB_CLK_CTRL,
		CKG_APB_UART0_EN|CKG_APB_GPIO_EN|CKG_APB_WDT0_EN|CKG_APB_SYST_EN,
		CKG_APB_UART0_EN|CKG_APB_GPIO_EN|CKG_APB_WDT0_EN|CKG_APB_SYST_EN);
	SET_REG_M(REG_CPU_CORE_CTRL, CKG_CPU_TS_EN, CKG_CPU_TS_EN); //arch timer enable
	//clk_cpu_core:1008M
	SET_REG_M(CPU_CLK_SEL,0x00000c03,0x00000c03);
	//clk_glb_apb 200MHz
	SET_REG_M(CKG_TOP_APB_CFG,0x3,0x3);
	//clk_glb_cpu2lvd 400MHz
	SET_REG_M(CKG_GLB_CPU2LVD_CFG,0x3,0x3);

	// clk_gmac_mtx=200M gmac0/1/aes/sdio1
	SET_REG_M(CK_CTRL0,0x30,0x30);

	// clk_emmc_mtx=200M sdio0/emmc/usb2/usb3
	SET_REG_M(NNP_SYS_CLK_RST_CTRL,0x6,0x6);
	// clk_hash=100M
	SET_REG_M(CKG_HASH_CFG,0x2,0x3);
	// clk_rsa=450M
	SET_REG_M(CKG_RSA_CFG,0x3,0x3);

	spll_fix_to_297_1m();

	//only mac0 and internal phy init
	SET_REG(0x24100178, 0x00000a00);
	SET_REG(0x2410017c, 0x001ffeaa);
	SET_REG_M(0x24000090, 4 << 16, 7 << 16);
}

int board_init(void)
{
	clock_init();
	/* set all dma handshake to dma0 */
	SET_REG(REG_DMA_SEL_LOW, 0);
	SET_REG(REG_DMA_SEL_HIGH, 0);
	SET_REG(REG_DMA0_AXCACHE, 0);
	SET_REG(REG_DMA1_AXCACHE, 0);
	/* Enable Ctrlc */
	/*console_init_f();*/
	gd->bd->bi_arch_number = MACH_TYPE_FH8210;
	/* adress of boot parameters */
	gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
	fh_pinctrl_init(CEN_PIN_REG_BASE + 0x4);
	if (get_boot_media() > 2)
		fh_pinctrl_sdev("EMMC", 0);
	/*hw_board_wdt_init();*/
#ifdef CONFIG_USB
	// fh_usb_power_enable();
#endif
#ifdef CONFIG_FH_MC_DMA
	 dma_init();
#endif

#if defined CONFIG_FH_AES_V2 && defined CONFIG_FH_ALGORITHM_CORE
	fh_aes_probe();
#endif

#ifdef CONFIG_FH_SPI
extern struct spi_slave *fh_spi_setup_slave(unsigned int bus, unsigned int cs,
				unsigned int max_hz, unsigned int mode);
	spi_init();
	spi_bus_register(0, fh_spi_setup_slave);
	spi_bus_register(1, fh_spi_setup_slave);
#endif
	return 0;
}

int dram_init(void)
{
	gd->ram_size = PHYS_SDRAM_SIZE;
	return 0;
}

void dram_init_banksize(void)
{
	gd->bd->bi_dram[0].start = PHYS_SDRAM;
	gd->bd->bi_dram[0].size = PHYS_SDRAM_SIZE;
}

void reset_cpu (ulong addr)
{
	SET_REG(REG_WR_PROTECT, 0x5a5a5a5a);
	SET_REG(REG_GLB_RESET, SW_GLB_RST);
}

#ifdef CONFIG_RESET_PHY_R
void reset_phy(void)
{
#ifdef CONFIG_MACB
	/*
	 * Initialize ethernet HW addr prior to starting Linux,
	 * needed for nfsroot
	 */
	eth_init(gd->bd);
#endif
}
#endif

void enable_caches(void)
{
	// empty func to avoid WARNING: Caches not enabled
}

#ifdef CONFIG_FH_MMC

void fh_mci_sys_init(unsigned int slot_id)
{
	unsigned int reg;

	if (slot_id == 1) {
		/*clk gate*/
		reg = readl(CK_CTRL0);
		reg |= (SDIO1_EN | SDIO1_DLLREF_EN);
		writel(reg, CK_CTRL0);

		reg = readl(CK_CTRL2);
		reg |= (AXI_SDIO1_EN);
		writel(reg, CK_CTRL2);


		/*sel clk 50M*/
		reg = readl(CK_CTRL0);
		reg &= (~SDIO1_CLK_SEL);
		reg |= 0x1 << 18;
		writel(reg, CK_CTRL0);
		
 		/*config dll*/
		writel(0x104, SDIO1_CTRL0);
		writel(0x4204, SDIO1_DLLINCTRLREG_SPL);
		writel(0x5204, SDIO1_DLLINCTRLREG_SPL);
		writel(0x4204, SDIO1_DLLINCTRLREG_DRV);
		writel(0x5204, SDIO1_DLLINCTRLREG_DRV);
		do {
			reg = readl(SDIO1_DLLOUTREG_DRV);
		}while(!(reg & 0x01));

		do {
			readl(SDIO1_DLLOUTREG_SPL);
		}while(!(reg & 0x01));

		writel(0x20, SDIO1_DLLININFOREG_SPL);
		writel(0x7204, SDIO1_DLLINCTRLREG_SPL);
		writel(0x5204, SDIO1_DLLINCTRLREG_SPL);
		writel(0x7f, SDIO1_DLLININFOREG_DRV);
		writel(0x7204, SDIO1_DLLINCTRLREG_DRV);
		writel(0x5204, SDIO1_DLLINCTRLREG_DRV);
		writel(0x924, SDIO1_CTRL0);

		/*rst sdio*/
		reg = readl(SOFT_RST0);
		reg |= SDIO1_SOFT_RST;
		udelay(1000);
		writel(reg, SOFT_RST0);
		reg &= (~SDIO1_SOFT_RST);
		udelay(1000);
		writel(reg, SOFT_RST0);
	} else {
		/*clk gate*/
		reg = readl(SDIO0_CLK_RST_CTRL);
		reg |= (SDIO0_EN | SDIO0_DLLREF_EN | AXI_SDIO0_EN);
		writel(reg, SDIO0_CLK_RST_CTRL);


		/*sel clk 50M*/
		reg = readl(SDIO0_CLK_RST_CTRL);
		reg &= (~SDIO0_CLK_SEL);
		reg |= 0x1 << 3;
		writel(reg, SDIO0_CLK_RST_CTRL);
	
 		/*config dll*/
		writel(0x104, SDIO0_CTRL0);
		writel(0x4204, SDIO0_DLLINCTRLREG_SPL);
		writel(0x5204, SDIO0_DLLINCTRLREG_SPL);
		writel(0x4204, SDIO0_DLLINCTRLREG_DRV);
		writel(0x5204, SDIO0_DLLINCTRLREG_DRV);
		do {
			reg = readl(SDIO0_DLLOUTREG_DRV);
		}while(!(reg & 0x01));

		do {
			readl(SDIO0_DLLOUTREG_SPL);
		}while(!(reg & 0x01));

		writel(0x20, SDIO0_DLLININFOREG_SPL);
		writel(0x7204, SDIO0_DLLINCTRLREG_SPL);
		writel(0x5204, SDIO0_DLLINCTRLREG_SPL);
		writel(0x7f, SDIO0_DLLININFOREG_DRV);
		writel(0x7204, SDIO0_DLLINCTRLREG_DRV);
		writel(0x5204, SDIO0_DLLINCTRLREG_DRV);
		writel(0x924, SDIO0_CTRL0);

		/*rst sdio*/
		reg = readl(SDIO0_CLK_RST_CTRL);
		reg |= SDIO0_SOFT_RST;
		writel(reg, SDIO0_CLK_RST_CTRL);
		udelay(1000);
		writel(reg, SDIO0_CLK_RST_CTRL);
		reg &= (~SDIO0_SOFT_RST);
		udelay(1000);
		writel(reg, SDIO0_CLK_RST_CTRL);
	}
}
#endif


#ifdef CONFIG_MSHC_LITE_FH

void soc_mshc_ctrl_reset(void *p_rev){

	unsigned int cfg;
	unsigned int timeout = 0xffff;
	//emmc reset

	SET_REG_M(REG_EMMC_CLK_RST_CTRL1, 3, 3);
	udelay(25);
	while (GET_REG(REG_EMMC_CLK_RST_CTRL1) & (3)){
		timeout--;
		if(timeout == 0){
				printf("mshc reset timeout !!!!!\n");
				return;
		}
	}
	// //phy_sample_delay0
	SET_REG_M(REG_EMMC_CARD_PAD_SEL, 3 << 5, 7 << 5);
	//phy_sample_delay1
	SET_REG_M(REG_EMMC_CARD_PAD_SEL, 4 << 8, 7 << 8);
	//printf("REG_EMMC_CARD_PAD_SEL is %x\n",GET_REG(REG_EMMC_CARD_PAD_SEL));
	//disable emmc_conflict_detect_en
	SET_REG_M(REG_EMMC_CARD_PAD_SEL, 0, 1 << 4);
	//card_write_prot
	//SET_REG_M(REG_EMMC_CARD_PAD_SEL, 0, 1 << 3);
	//card_detect_n
	// SET_REG_M(REG_EMMC_CARD_PAD_SEL, 0, 1);
}

struct mshc_lite_emmc_card_info emmc_card_array[] = {
	{
		.mmc_name = "016G30",
	},
	{
		.mmc_name = "BJTD4R",
	},
	{
		.mmc_name = "8GTF4R",
		//.flag = FORCE_USR_SETTING,
		//hs200 		: .sup_mode = MMC_MODE_HS200_1_8V
		//hs400_classic : .sup_mode = MMC_MODE_HS400_1_8V
		//hs400_es		: .sup_mode = MMC_MODE_HS400ES
		//.sup_mode = MMC_MODE_HS200_1_8V,
		// .sup_mode = MMC_MODE_HS400_1_8V,
		.sup_mode = MMC_MODE_HS400ES,
	    .hs200_tx_data = 0x50,
		.hs200_rx_cmd = 0x88,
		.hs200_rx_data = 0x84,

	    .hs400_tx_data = 0x6c,
		.hs400_rx_cmd = 0x68,
		.hs400_rx_data = 0x34,
	},
	{
		.mmc_name = "008GB0",
	},
	{
		.mmc_name = "032G32",
	},
	{
		.mmc_name = "AJTD4R",
	},
	{
		.mmc_name = "004GA0",
	},
	{
		.mmc_name = "008G30",
	},
	{
		.mmc_name = "004GA1",

	},
	{
		.mmc_name = "SBS3S2",
	},
	{
		.mmc_name = "08A391",
	},
	{
		.mmc_name = "58A43A",
	},
	{
		.mmc_name = "064G30",
	},
	{
		.mmc_name = "064G02",
	},
	{
		.mmc_name = "dummy",
	},

	{0},
};


void soc_mshc_clk_set(void *p_rev,unsigned int clock)
{
	//usr may send 52M,actual is 50M
	unsigned int emmc_clk[] = {
			24000000, 52000000, 200000000};
	unsigned int base_clk = 0, div;
	unsigned int i, cfg;

	if(clock > 200000000){
		printf("not support clk %d; max is 200000000\n",clock);
		return;
	}
	if(clock == 0)
		return;
	//clk disable first...
	//close clk_emmc
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			0, 1 << 18);
	//close clk_emmc_2x
	SET_REG_M(REG_EMMC_CLK_RST_CTRL1,
			0, 1 << 4);

	//if usr clk set 200M. 2x need 400M..so clk max is 200M for emmc card
	if (clock == 200000000) {
		//clk sel 400M in
		SET_REG_M(REG_EMMC_CLK_RST_CTRL1,
				(3 << 2), 0x0000000c);
		//clk_emmc div 2
		SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
				(1<<24), 0x7f000000);
		//2x clk no div
		SET_REG_M(REG_EMMC_CLK_RST_CTRL1,
				0, 0x00001fc0);

		//enable clk emmc
		SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
				1 << 18, 1 << 18);
		//enable clk emmc 2x
		SET_REG_M(REG_EMMC_CLK_RST_CTRL1,
				1 << 4, 1 << 4);

		return;
	}
	for (i = 0; i < ARRAY_SIZE(emmc_clk); i++) {
		if (clock <= emmc_clk[i]) {
			base_clk = emmc_clk[i];
			break;
		}
	}

	div = base_clk / clock;
	// if (base_clk % clock)
	// 	printf("unsupported freq: %d\n", clock);

	if (div == 0)
		printf("div can't be 0\n");

	// if(div < 2)
	//      printf("2x clk div not sup...\n");
	//clk sel
	SET_REG_M(REG_EMMC_CLK_RST_CTRL1,
			(i << 2), 0x0000000c);
	//printf("sel base clk[%d] : div[%d]\n",base_clk, div);
	//set clk_emmc
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			((div-1)<<24), 0x7f000000);

	//enable clk emmc
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			1 << 18, 1 << 18);

}

#define INIT_EMMC_CLK  400000
void soc_mshc_clk_init(void *p_rev){
	unsigned int cfg;
	//init clk_emmc 400K

	soc_mshc_clk_set(p_rev, INIT_EMMC_CLK);
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			0, 0x00fc0000);
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			2 << 2, 3 << 2);
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			1 << 4, 3 << 4);
	//div 24
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			24 << 6, 0x1f << 6);
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			1 << 11, 3 << 11);
	//div 24
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			24 << 13, 0x1f << 13);
	SET_REG_M(REG_EMMC_CLK_RST_CTRL0,
			0x00cc0000, 0x00fc0000);
	//clk emmc mtx
	//clk axi emmc
	//clk ahb emmc
}


struct mshc_lite_plat_info mshc_plat = {
	.id = 0,
	.name = "fh_mshc",
	.ctrl_regs = EMMC_REG_BASE,
	.phy_regs = EMMC_PHY_REG_BASE,
	.soc_clk_init = soc_mshc_clk_init,
	//.phy_init = soc_mshc_phy_init,
	.soc_ctrl_reset = soc_mshc_ctrl_reset,
	.emmc_info_list = emmc_card_array,
	.clk_set = soc_mshc_clk_set,
};

#endif


#ifdef CONFIG_GENERIC_MMC
#ifdef CONFIG_FH_MMC
extern int fh_mmc_init(int index);
#endif

int board_mmc_init(bd_t *bis)
{
    int ret = 0;

#ifdef CONFIG_MSHC_LITE_FH
	if (get_boot_media() > 2){
		ret = fh_mshc_lite_int(&mshc_plat);
		if (ret) {
				printf("No EMMC device found !\n");
		}
	}
#endif

#ifdef CONFIG_FH_MMC
#ifdef FH_MMC_SDC0
	ret = fh_mmc_init(0);
	if (ret)
	    printf("No SD0 device found !\n");
#endif
#ifdef FH_MMC_SDC1
    ret = fh_mmc_init(1);
    if (ret)
        printf("No SD1 device found !\n");
#endif
#endif
    return ret;
}
#endif

void do_arc_jump(ulong addr)
{
	// hold reset
	SET_REG_M(REG_CPU_SOFT_RST, MCU_ALL_SOFT_RST, MCU_ALL_SOFT_RST);

	printf("addr: 0x%x\n", addr);

	SET_REG(REG_MCU_ADDR_MSB_SEL, MCU_REMAP);
	SET_REG(REG_MCU_START_AWADDR, addr);
	SET_REG(REG_MCU_START_ARADDR, addr);
	SET_REG(REG_COMPARE_AWADDR, 0xffffff); // remap space: 10M
	SET_REG(REG_COMPARE_ARADDR, 0xffffff);

	SET_REG_M(REG_CPU_SOFT_RST, 0, MCU_ALL_SOFT_RST);
}

int mcu_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    ulong   addr;

    if (argc < 2) {
        cmd_usage(cmdtp);
        return 1;
    }

    addr = simple_strtoul(argv[1], NULL, 16);

    printf ("## Starting application at 0x%08lX ...\n", addr);

    do_arc_jump(addr);

    return 0;
}

U_BOOT_CMD(
    mcu_go, CONFIG_SYS_MAXARGS, 1,  mcu_go,
    "start application at address 'addr'",
    "addr [arg ...]\n    - start application at address 'addr'\n"
    "      passing 'arg' as arguments"
);

#if defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_XHCI_DWC3)

#if defined(CONFIG_USB_DWC3_GADGET)
static struct dwc3_device usb_otg_ss = {
	.maximum_speed = USB_SPEED_HIGH,
	.base = CONFIG_USB_XHCI_DWC3_REG_BASE,
	.tx_fifo_resize = false,
	.index = 0,
	.dr_mode = USB_DR_MODE_PERIPHERAL,
	.dis_u2_susphy_quirk = true,
	.dis_u3_susphy_quirk = true,
};
#endif

int board_usb_init(int index, enum usb_init_type init)
{
	uint32_t pwren_gpio;

	writel(readl(NNP_SYS_APB_REG_BASE + 0x300) | (1<<0), NNP_SYS_APB_REG_BASE + 0x300);
	pwren_gpio = 60;

	if (init == USB_INIT_HOST) {
		gpio_request(pwren_gpio, "usb_pwren");
		gpio_direction_output(pwren_gpio, 1);
		gpio_free(pwren_gpio);
	} else if (init == USB_INIT_DEVICE) {
		gpio_request(pwren_gpio, "usb_pwren");
		gpio_direction_output(pwren_gpio, 0);
		gpio_free(pwren_gpio);
	} else
		return -2;

#if defined(CONFIG_USB_DWC3_GADGET)
	dwc3_uboot_init(&usb_otg_ss);
#endif

	return 0;
}

int board_usb_cleanup(int index, enum usb_init_type init)
{
}
#endif

#ifdef CONFIG_OPTEE
#include <dm/platdata.h>
#include <linux/arm-smccc.h>

static void optee_smccc_smc(unsigned long a0, unsigned long a1,
			    unsigned long a2, unsigned long a3,
			    unsigned long a4, unsigned long a5,
			    unsigned long a6, unsigned long a7,
			    struct arm_smccc_res *res)
{
	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
}

struct {
	void *invoke_fn;
} optee_pdata = {optee_smccc_smc};

U_BOOT_DEVICE(optee) = {
	.name = "optee",
	.platdata = &optee_pdata,
};
#endif
